//
// security_editor.cpp
// gcs
// Grand Central Station, controlling process of the Protea Application Server / Business Logic Transactions for the SAI security editor
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

#include "../SDK/ToolsInterface.h"
#include "../AppHandler/parms_xml.h"
#include "../AppHandler/security_helper.h"
#include "gcs.h"
#include "security_editor.h"

XCAuthenticateEditor::XCAuthenticateEditor( XCAppInterface* myapp ) {
	AuthInit( NULL );
	app = myapp;
};

void XCAuthenticateEditor::AddApplication( int app_nbr, bool default_allow_access ) {
	XCSecurityApplication* newapp = new XCSecurityApplication( app_nbr, default_allow_access );
	apps.AddApplication( newapp );
	Output();
};

void XCAuthenticateEditor::AddApplicationItem( XCSecurityItems* items, long item_number, bool default_allow_access ) {
	XCSecurityItem* si = items->AddItem( item_number );
	si->default_allow = default_allow_access;
	Output();
};

void XCAuthenticateEditor::RemoveApplicationItem( XCSecurityItems* items, XCSecurityItem* item ) {
	Output();
};

void XCAuthenticateEditor::ChangeApplicationSecurity( XCSecurityRuleList* rules, const char* user_name, bool new_value ) {
	INFUNC( XCAuthenticateEditor::ChangeApplicationSecurity, NULL );

	if (user_name) {
		// Now determine the user/group index number
		XCUserInformation* user = users.Find( user_name );
		XCGroupInformation* group = groups.Find( user_name );
		if (!user && !group)
			RAISEFIELDERROR_1PARAM( ERR_DOESNT_EXIST, FLD_USER_NAME, ERR_USER );

		// Determine the user/group index
		int index;
		if (user)
			index = user->index;
		else
			index = -group->index;

		// Determine if this rule already exists
		XCSecurityRule* rule = rules->rules;
		while (rule && rule->user_group_index != index)
			rule = rule->next_rule;
		if (!rule)
			RAISEFIELDERROR_1PARAM( ERR_DOESNT_EXIST, FLD_USER_NAME, user ? ERR_USER : ERR_GROUP );

		rule->allow = new_value;
	} else {
		// Change the default for the item
		rules->default_allow = new_value;
	};

	Output();

	OUTFUNC();
};

void XCAuthenticateEditor::AddApplicationSecurity( XCSecurityRuleList* rules, const char* user_name, bool default_allow_access ) {
	INFUNC( AddApplicationSecurity, NULL );

	// Now determine the user/group index number
	XCUserInformation* user = users.Find( user_name );
	XCGroupInformation* group = groups.Find( user_name );
	if (!user && !group)
		RAISEFIELDERROR_1PARAM( ERR_DOESNT_EXIST, FLD_USER_NAME, ERR_USER );

	// Determine the user/group index
	int index;
	if (user)
		index = user->index;
	else
		index = -group->index;

	// Determine if this rule already exists
	XCSecurityRule* rule = rules->rules;
	while (rule && rule->user_group_index != index)
		rule = rule->next_rule;
	if (rule)
		RAISEFIELDERROR_1PARAM( ERR_ALREADY_EXISTS, FLD_USER_NAME, user ? ERR_USER : ERR_GROUP );

	// Add to the rules
	rules->Add( this, user_name, user ? true : false, default_allow_access );

	Output();

	OUTFUNC();
};


void XCAuthenticateEditor::RemoveApplicationSecurity( XCSecurityRuleList* rules, const char* user_name ) {
	INFUNC( RemoveApplicationSecurity, NULL );

	// Now determine the user/group index number
	XCUserInformation* user = users.Find( user_name );
	XCGroupInformation* group = groups.Find( user_name );
	if (!user && !group)
		RAISEFIELDERROR_1PARAM( ERR_DOESNT_EXIST, FLD_USER_NAME, ERR_USER );

	// Determine the user/group index
	int index;
	if (user)
		index = user->index;
	else
		index = -group->index;

	// Determine if this rule already exists
	XCSecurityRule* rule = rules->rules;
	while (rule && rule->user_group_index != index)
		rule = rule->next_rule;
	if (!rule)
		RAISEFIELDERROR_1PARAM( ERR_DOESNT_EXIST, FLD_USER_NAME, user ? ERR_USER : ERR_GROUP );

	// Add to the rules
	rules->Remove( index );

	Output();

	OUTFUNC();
};


void XCAuthenticateEditor::AddGroup( const char* name, const char* description ) {
	groups.Add( name, description );
	Output();

	// Send an update message to the children
	security_info_manager manager;
	manager.group_name = (char*)name;
	manager.description = (char*)description;
	manager.SubmitChange( SECURITY_ADD_GROUP, app );
};

void XCAuthenticateEditor::RemoveGroup( const char* name ) {
	XCGroupInformation* group = groups.Find( name );
	apps.RemoveItem( -group->index );
	groups.Remove( group );
	delete group;
	Output();

	// Send an update message to the children
	security_info_manager manager;
	manager.group_name = (char*)name;
	manager.SubmitChange( SECURITY_DEL_GROUP, app );
};

void XCAuthenticateEditor::AddUser( const char* name, const char* password, const char* description ) {
	const char* submit_pass;

	if (password && *password) {
		//@ Do an MD5 encryption on the password
		char result[ 25 ];
		EncryptPassword( password, result );
		submit_pass = result;
	} else
		submit_pass = password;

	users.Add( name, submit_pass, description );
	Output();

	// Send an update message to the children
	security_info_manager manager;
	manager.user_name = (char*)name;
	manager.user_password = (char*)submit_pass;
	manager.description = (char*)description;
	manager.SubmitChange( SECURITY_ADD_USER, app );
};

void XCAuthenticateEditor::UpdateUser( const char* name, const char* password, const char* description ) {
	XCUserInformation* user = users.Find( name );

	// Remove old values and replace with new
	if (description) {
		if (user->description)
			free( user->description );
		if (*description) {
			user->description = strdup( description );
		} else
			user->description = NULL;
	};

	// Remove old values and replace with new
	if (password) {
		if (user->password)
			free( user->password );
		if (*password) {
			//@ Do an MD5 encryption on the password
			char result[ 255 ];
			EncryptPassword( password, result );
			user->password = strdup( result );
		} else
			user->password = NULL;
	};

	Output();

	// Send an update message to the children
	security_info_manager manager;
	manager.user_name = (char*)user->name;
	manager.user_password = (char*)user->password;
	manager.description = (char*)user->description;
	manager.SubmitChange( SECURITY_CHG_USER, app );
};

void XCAuthenticateEditor::RemoveUser( const char* name ) {
	XCUserInformation* user = users.Find( name );

	apps.RemoveItem( user->index );
	groups.RemoveUser( user );
	users.Remove( user );
	delete user;
	Output();

	// Send an update message to the children
	security_info_manager manager;
	manager.user_name = (char*)name;
	manager.SubmitChange( SECURITY_DEL_USER, app );
};

void XCAuthenticateEditor::AddUserToGroup( const char* user_name, const char* group_name ) {
	XCUserInformation* user = users.Find( user_name );
	XCGroupInformation* group = groups.Find( group_name );

	group->AddUser( user );
	Output();

	// Send an update message to the children
	security_info_manager manager;
	manager.user_name = (char*)user_name;
	manager.group_name = (char*)group_name;
	manager.SubmitChange( SECURITY_ADD_USER_TO_GROUP, app );
};

void XCAuthenticateEditor::RemoveUserFromGroup( const char* user_name, const char* group_name ) {
	XCUserInformation* user = users.Find( user_name );
	XCGroupInformation* group = groups.Find( group_name );
	group->RemoveUser( user );
	Output();

	// Send an update message to the children
	security_info_manager manager;
	manager.user_name = (char*)user_name;
	manager.group_name = (char*)group_name;
	manager.SubmitChange( SECURITY_DEL_USER_FROM_GROUP, app );
};

void XCAuthenticateEditor::OutputRuleList( fstream& output, char* nice, XCSecurityRuleList* rules ) {
	output << "Default='";
	if (rules->default_allow)
		output << "Allow";
	else
		output << "Deny";
	output << "'>" << endl;

	XCSecurityRule* temp = rules->rules;
	while (temp) {
		if (temp->user_group_index > 0) {
			const XCUserInformation* user = users.Find( temp->user_group_index );
			output << nice << "<User Name='" << user->name;
		} else {
			const XCGroupInformation* group = groups.Find( -temp->user_group_index );
			output << nice << "<Group Name='" << group->name;
		};

		output << "' Mode='";
		if (temp->allow)
			output << "Allow";
		else
			output << "Deny";

		output << "' />" << endl;
		temp = temp->next_rule;
	};
};

void XCAuthenticateEditor::OutputItems( fstream& output, char* nice, char* name, XCSecurityItems* items ) {
	OutputRuleList( output, nice, items );

	XCSecurityItem* temp = items->items;
	while (temp) {
		output << nice << "<" << name << " Number='" << temp->item_number << "' ";
		OutputRuleList( output, nice, temp );
		output << nice << "</" << name << ">" << endl;
		temp = temp->next;
	};
};

// Rewrites the security xml file
void XCAuthenticateEditor::Output() {
	XCUserPointer* pusers;
	fstream output;

	output.open( "security.xml", ios::out );

	output << "<Security>" << endl;

	// Output the user list
	output << "\t<UserList>" << endl;
	pusers = users.GetUserList();
	while (pusers) {
		XCUserInformation* user = pusers->info;
		output << "\t\t<UserDef Name='" << user->name << "'";
		output << " Password='" << user->password << "'";
		if (user->description)
			output << " Description='" << user->description << "'";
		output << " />" << endl;
		pusers = pusers->next;
	};
	output << "\t</UserList>" << endl;

	// Output the group list
	output << "\t<GroupList>" << endl;
	XCGroupInformation* group = groups.GetGroupList();
	while (group) {
		output << "\t\t<GroupDef Name='" << group->name << "'";
		if (group->description)
			output << " Description='" << group->description << "'";
		output << ">" << endl;
		pusers = group->users;
		while (pusers) {
			output << "\t\t\t<User Name='" << pusers->info->name << "' />" << endl;
			pusers = pusers->next;
		};
		output << "\t\t</GroupDef>" << endl;
		group = group->next_group;
	};
	output << "\t</GroupList>" << endl;

	// For each application:
	XCSecurityApplication* temp = apps.applist;
	while (temp) {
		// Output application information
		output << "\t<Application Number='" << temp->number << "' ";
		OutputRuleList( output, "\t\t", temp );

		// Output transaction information for this application
		output << "\t\t<Transactions ";
		OutputItems( output, "\t\t\t", "Transaction", &temp->trans );
		output << "\t\t</Transactions>" << endl;

		// Output capabilities information for this application
		output << "\t\t<Capabilities ";
		OutputItems( output, "\t\t\t", "Capability", &temp->caps );
		output << "\t\t</Capabilities>" << endl;

		// Output data-items information for this application
		output << "\t\t<DataItems ";
		OutputItems( output, "\t\t\t", "DataItem", &temp->data_items );
		output << "\t\t</DataItems>" << endl;

		// Close application tag
		output << "\t</Application>" << endl;
		temp = temp->next;
	};

	output << "</Security>" << endl;
	output.close();
};
